{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.models import Model\n",
    "from keras.layers import Lambda,Activation,Dense,Conv2D,Input,BatchNormalization,MaxPooling2D,Flatten\n",
    "import keras.backend as K\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_tensor_1 = Input([32,32,3])\n",
    "input_tensor_2 = Input([4,])\n",
    "input_target = Input([2,])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "#loss 计算函数\n",
    "\n",
    "#mean 求平均 sum求和 square 平方 abs 绝对值\n",
    "def cus_loss1(y_true,y_pred):\n",
    "    return K.mean(K.abs(y_true-y_pred))\n",
    "    \n",
    "def cus_loss2(y_true,y_pred):\n",
    "    return K.mean(K.abs(y_true-y_pred))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "#网络结构\n",
    "#第一组\n",
    "x = BatchNormalization(axis=1,name='BN-1')(input_tensor_1)\n",
    "\n",
    "x = Conv2D(filters = 32,kernel_size = (3,3),padding='same')(x)\n",
    "x = Activation('relu')(x)\n",
    "x = MaxPooling2D(pool_size=(2,2))(x)\n",
    "\n",
    "x = Conv2D(filters = 32,kernel_size = (3,3),padding='same')(x)\n",
    "x = Activation('relu')(x)\n",
    "x = MaxPooling2D(pool_size=(2,2))(x)\n",
    "\n",
    "x = Flatten()(x)\n",
    "x = Dense(units=16)(x)\n",
    "out2 = Dense(units=2)(x)\n",
    "\n",
    "#第二组\n",
    "y = Dense(units = 32) (input_tensor_2)\n",
    "out1 = Dense(units =2 )(y)\n",
    "\n",
    "#第三组\n",
    "z = Dense(units = 8)(input_target)\n",
    "out3 = Dense(units=2)(z)\n",
    "\n",
    "#自定义loss\n",
    "loss1 = Lambda(lambda x:cus_loss1(*x),name='loss1')([out2,out1]) # Realdata - pred data = loss\n",
    "loss2 = Lambda(lambda x:cus_loss2(*x),name='loss2')([out3,out2])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: UserWarning: Update your `Model` call to the Keras 2 API: `Model(inputs=[<tf.Tenso..., outputs=[<tf.Tenso...)`\n",
      "  \"\"\"Entry point for launching an IPython kernel.\n"
     ]
    }
   ],
   "source": [
    "model = Model(input=[input_tensor_1,input_tensor_2,input_target],output=[out1,out2,out3,loss1,loss2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "#获取loss\n",
    "loss_layer1 = model.get_layer('loss1').output\n",
    "loss_layer2 = model.get_layer('loss2').output\n",
    "\n",
    "#向模型添加loss\n",
    "model.add_loss(loss_layer1)\n",
    "model.add_loss(loss_layer2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "#loss +None 表述不需要梯度返回， 有多少个 out 就有多少个none\n",
    "model.compile(optimizer='sgd',loss=[None,None,None,None,None])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "#dataset\n",
    "def data_gen(number):\n",
    "    for i in range(number):\n",
    "        yield [np.random.normal(1,1,size=(1,32,32,3)),\n",
    "                            np.random.normal(1,1,size=(1,4)),\n",
    "                                             np.random.normal(1,1,size=(1,2))],[]\n",
    "dataset = data_gen(1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "20/20 [==============================] - 7s 353ms/step - loss: 2.1724\n",
      "Epoch 2/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.7822\n",
      "Epoch 3/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.4515\n",
      "Epoch 4/10\n",
      "20/20 [==============================] - 0s 13ms/step - loss: 0.2656\n",
      "Epoch 5/10\n",
      "20/20 [==============================] - 0s 13ms/step - loss: 0.1545\n",
      "Epoch 6/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.1432\n",
      "Epoch 7/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.1468\n",
      "Epoch 8/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.1247\n",
      "Epoch 9/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.1316\n",
      "Epoch 10/10\n",
      "20/20 [==============================] - 0s 12ms/step - loss: 0.1121\n"
     ]
    }
   ],
   "source": [
    "train = model.fit_generator(dataset,epochs=10,steps_per_epoch=20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.utils.vis_utils import plot_model\n",
    "plot_model(model=model,to_file='model_plain-loss.png',show_shapes=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
